﻿using Sharp7;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Xml;
using PIMS.Interface;
using SqlSugar;
using PIMS.XmlModel;
using PIMS.XmlModel.Snap7;

namespace PIMS.Commication.Snap7
{
    /// <summary>
    /// 设置IP地址和Slot槽号
    /// Open()方法打开连接
    /// Close()方法关闭连接
    /// </summary>
    public class Simatic: Equip, ICommunication
    {
        //string IP="10.10.10.10"; plcip
        //int Slot=0; 槽号,1200~1500设置为0,300系列设置为2
        private DBInfo dbi;

        private string name = "";

        /// <summary>
        /// PLC在线
        /// </summary>
        public bool IsConnected
        {
            get
            {
                return base.isOnline;
            }           
        }

        public IPLCXModel xModel { get; set; }

        #region plc_read_db 支持WSTRING
        /// <summary>
        /// 读DB里的各种类型值 glb
        /// 注：INT要高8位和低8位转换
        /// </summary>
        /// <param name="DBNr"></param>
        /// <param name="Addr"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        private bool plc_read_db(int DBNr, int Addr, int bit, out bool result)
        {
            byte[] buff;
            bool ret = this.ReadBlock(DBNr.ToString(), Addr, 1, out buff);
            if (buff.Length > 0)
                result = Sharp7.S7.GetBitAt(buff, 0, bit);
            else
                result = false;
            return ret;
        }
        private bool plc_read_db(int DBNr, int Addr, out byte result)
        {
            byte[] buff;
            bool ret = this.ReadBlock(DBNr.ToString(), Addr, 1, out buff);
            if (buff.Length > 0)
                result = buff[0];
            else
                result = 0;
            return ret;
        }
        private bool plc_read_db(int DBNr, int Addr, out int result)
        {
            byte[] buff;
            bool ret = this.ReadBlock(DBNr.ToString(), Addr,2, out buff);
            if (buff.Length >= 2)
                result = BitConverter.ToInt16(buff, 0);
            else
                result = 0;
            result=this.Swap(result);
            return ret;
        }
        private bool plc_read_db(int DBNr, int Addr, out string result)
        {
            byte[] buff;
            //字符串地址要加1
            bool ret = this.ReadBlock(DBNr.ToString(), Addr+1, 254, out buff);
            result = "";
            //this.Swap(buff);            
            //MessageBox.Show(Util.byteToHexStr(buff));
            if (buff.Length > 0)
            {
               //byte a = buff[1];
               //buff[1] = buff[0];
               //buff[0] = a;
               byte[] rev=new byte[buff[0]];
               Array.Copy(buff,1,rev,0,rev.Length);
               result = Encoding.ASCII.GetString(rev); 
            }
            return ret;
        }

        private bool plc_read_db(int DBNr, int Addr,int bit, out string result)
        {
            byte[] buff;
            //字符串地址要加1
            bool ret = this.ReadBlock(DBNr.ToString(), Addr + 1, bit, out buff);
            result = "";
            //this.Swap(buff);            
            //MessageBox.Show(Util.byteToHexStr(buff));
            if (buff.Length > 0)
            {
                //byte a = buff[1];
                //buff[1] = buff[0];
                //buff[0] = a;
                byte[] rev = new byte[buff[0]];
                Array.Copy(buff, 1, rev, 0, rev.Length);
                result = Encoding.ASCII.GetString(rev);
            }
            return ret;
        }

        private bool plc_read_db(int DBNr, int Addr, out float result)
        {
            byte[] buff;
            bool ret = this.ReadBlock(DBNr.ToString(), Addr, 4, out buff);
            if (buff.Length > 0)
            {
                result = Sharp7.S7.GetRealAt(buff, 0);
            }
            else
            {
                result = 0.0f;
            }
            return ret;
        }
        private bool plc_read_db(int DBNr, int Addr, int length, out byte[] result)
        {
            byte[] buff;
            bool ret = this.ReadBlock(DBNr.ToString(), Addr, length, out buff);
            result = new byte[length];
            if (buff.Length == length)
            {
                Array.Copy(buff,result,length);                
            }               
               
            return ret;
        }

        private bool plc_read_db_ws(int DBNr, int Addr, out string result)
        {
            byte[] buff;
            //字符串地址要加1
            bool ret = this.ReadBlock(DBNr.ToString(), Addr + 2, 254*2+2, out buff);
            result = "";
            if (buff.Length > 0)
            {
                byte[] rev = new byte[buff[1]*2];
                Array.Copy(buff, 2, rev, 0, rev.Length);
                //Unicode高低位颠倒
                for (int i = 0; i < rev.Length - 1; i += 2)
                {
                    var temp = rev[i];
                    rev[i] = rev[i + 1];
                    rev[i + 1] = temp;
                }
                result = Encoding.Unicode.GetString(rev);
            }
            return ret;
        }

        /// <summary>
        /// *2+2
        /// </summary>
        /// <param name="DBNr"></param>
        /// <param name="Addr"></param>
        /// <param name="bit"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        private bool plc_read_db_ws(int DBNr, int Addr, int bit,out string result)
        {
            byte[] buff;
            //字符串地址要加1
            bool ret = this.ReadBlock(DBNr.ToString(), Addr + 2, bit*2+2, out buff);
            result = "";
            if (buff.Length > 0)
            {
                byte[] rev = new byte[buff[1] * 2];
                Array.Copy(buff, 2, rev, 0, rev.Length);
                //Unicode高低位颠倒
                for (int i = 0; i < rev.Length - 1; i += 2)
                {
                    var temp = rev[i];
                    rev[i] = rev[i + 1];
                    rev[i + 1] = temp;
                }
                result = Encoding.Unicode.GetString(rev);
            }
            return ret;
        }
        #endregion

        #region plc_write_db,支持WSTRING
        /// <summary>
        /// 写DB里的各种类型值 glb
        /// </summary>
        /// <param name="DBNr"></param>
        /// <param name="Addr"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private bool plc_write_db(int DBNr, int Addr, int bit, bool value)
        {
            byte[] buff=new byte[1];
           // buff[0] = (value ? (byte)1 : (byte)0);
            this.ReadBlock(DBNr.ToString(), Addr, 1, out buff);
            Sharp7.S7.SetBitAt(buff,0, bit, value);
            return this.WriteBlock(DBNr, Addr, buff);
        }
        private bool plc_write_db(int DBNr, int Addr, byte value)
        {
            byte[] buff = new byte[1];
            buff[0] = value;
            return this.WriteBlock(DBNr, Addr, buff);
        }
        private bool plc_write_db(int DBNr, int Addr, int value)
        {
            byte[] buff=BitConverter.GetBytes(this.Swap(value));
            byte[] buff2 = new byte[2];
            Array.Copy(buff,buff2,2);
            return this.WriteBlock(DBNr, Addr, buff2);
        }
        private bool plc_write_db(int DBNr, int Addr, string value)
        {
            byte[] buff = Encoding.ASCII.GetBytes(value);
            byte[] buff2 = new byte[buff.Length+1];
            buff2[0] = (byte)buff.Length;
            Array.Copy(buff, 0, buff2, 1, buff.Length);            
            return this.WriteBlock(DBNr, Addr + 1, buff2);
        }
        private bool plc_write_db_chinese(int DBNr, int Addr, string value)
        {
            byte[] buff = Encoding.Default.GetBytes(value);
            //byte[] buff = Encoding.ASCII.GetBytes(value);
            byte[] buff2 = new byte[buff.Length + 1];
            buff2[0] = (byte)buff.Length;
            Array.Copy(buff, 0, buff2, 1, buff.Length);
            return this.WriteBlock(DBNr, Addr + 1, buff2);
        }
        private bool plc_write_db_ws(int DBNr, int Addr, string value)
        {
            byte[] buff = Encoding.Unicode.GetBytes(value);
            byte[] buff2 = new byte[buff.Length + 2];
            buff2[0] = (byte)(value.Length / 255);
            buff2[1] = (byte)(value.Length % 255);
            Array.Copy(buff, 0, buff2, 2, buff.Length);
            //Unicode高低位颠倒
            for (int i = 2; i < buff2.Length - 1; i+=2)
            {
                var temp = buff2[i];
                buff2[i] = buff2[i + 1];
                buff2[i + 1] = temp;
            }
            return this.WriteBlock(DBNr, Addr + 2, buff2);
        }
        private bool plc_write_db(int DBNr, int Addr, float value)
        {
            byte[] buff = new byte[4];
            Sharp7.S7.SetRealAt(buff, 0,value);
            return this.WriteBlock(DBNr, Addr, buff);
        }
        private bool plc_write_db(int DBNr, int Addr, byte[] value)
        {
            return this.WriteBlock(DBNr, Addr, value);
        }
        #endregion

        private int GetDBNum(string dbName)
        {
            int dbi = 0;
            if (dbName.ToUpper().StartsWith("DB"))
            {
                int.TryParse(dbName.ToUpper().Substring(2), out dbi);
            }
            return dbi;
        }

        /// <summary>
        /// 更新 支持定长STRING
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="startAddr"></param>
        /// <param name="bit"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        private string GetType(string addr, out int startAddr, out int bit)
        {
            string Addr = addr.ToUpper();
            bit = 0;
            if (Addr.StartsWith("INT"))
            {
                #region
                Addr = Addr.Substring(3);
                if (Addr.Contains(","))
                {
                    //Array
                    string[] strs = Addr.Split(',');
                    if (strs.Length == 2)
                    {
                        int.TryParse(strs[0], out startAddr);
                        int.TryParse(strs[1], out bit);
                    }
                    else
                    {
                        throw new Exception("PLC XML配置有误，地址：" + addr);
                    }
                }
                else
                {
                    int.TryParse(Addr, out startAddr);
                }
                return "int";
                #endregion
            }
            else if (Addr.StartsWith("STRING"))
            {
                #region
                if(Addr.Contains("."))
                {
                    string str=Addr.Substring(6);
                    int.TryParse(str.Split('.')[0], out startAddr);
                    int.TryParse(str.Split('.')[1], out bit);
                }
                else
                {
                    int.TryParse(Addr.Substring(6), out startAddr);
                    bit = 254;
                }
                return "string";
                #endregion
            }
            else if (Addr.StartsWith("WSTRING"))
            {
                #region
                //int.TryParse(Addr.Substring(7), out startAddr);
                //return "wstring";
                if (Addr.Contains("."))
                {
                    string str = Addr.Substring(7);
                    int.TryParse(str.Split('.')[0], out startAddr);
                    int.TryParse(str.Split('.')[1], out bit);
                }
                else
                {
                    int.TryParse(Addr.Substring(7), out startAddr);
                    bit = 254;
                }
                #endregion
            }
            else if (Addr.StartsWith("X"))
            {
                #region
                Addr = Addr.Substring(1);
                if (Addr.Contains("."))
                {
                    string[] strs = Addr.Split('.');
                    if (strs.Length == 2)
                    {
                        int.TryParse(strs[0], out startAddr);
                        int.TryParse(strs[1], out bit);
                    }
                    else
                    {
                        throw new Exception("PLC XML配置有误，地址：" + addr);
                    }
                }
                else
                {
                    int.TryParse(Addr, out startAddr);
                }
                return "bool";
                #endregion
            }
            else if (Addr.StartsWith("B"))
            {
                #region
                Addr = Addr.Substring(1);
                if (Addr.Contains(","))
                {
                    //Array
                    string[] strs = Addr.Split(',');
                    if (strs.Length == 2)
                    {
                        int.TryParse(strs[0], out startAddr);
                        int.TryParse(strs[1], out bit);
                    }
                    else
                    {
                        throw new Exception("PLC XML配置有误，地址：" + addr);
                    }
                }
                else
                {
                    int.TryParse(Addr, out startAddr);
                }
                return "byte";
                #endregion
            }
            else if (Addr.StartsWith("R"))
            {
                #region
                int.TryParse(Addr.Substring(1), out startAddr);
                return "real";
                #endregion
            }
            throw new Exception("PLC XML配置有误，地址：" + addr);
        }

        public Simatic(string xmlpath)
        {
            try
            {
                XmlDocument xmlDocument = new XmlDocument();
                XmlReaderSettings settings = new XmlReaderSettings();
                settings.IgnoreComments = true;
                XmlReader reader = XmlReader.Create(xmlpath, settings);
                xmlDocument.Load(reader);

                XmlNodeList nodeList = xmlDocument.GetElementsByTagName("Device");
                XmlNodeList nodePLC = xmlDocument.GetElementsByTagName("PLC");
                reader.Close();

                this.IP = nodeList[0].Attributes["Ip"].Value;
                this.name= nodeList[0].Attributes["deviceName"].Value;

                string type = nodeList[0].Attributes["Type"].Value;
                if(type.ToUpper()=="S7-300")
                {
                    this.Slot = 2;
                }
                else if (type.ToUpper() == "S7-400")
                {
                    this.Slot = 3;
                }

                int num = int.Parse(nodePLC[0].Attributes["Num"].Value);

                dbi.id = new string[num];
                dbi.db = new string[num];
                dbi.addr = new string[num];
                dbi.name = new string[num];

                int i = 0;
                foreach (XmlNode itemID in nodePLC[0].ChildNodes)
                {
                    if (i >= num)
                        break;
                    dbi.id[i] = itemID.Attributes["id"].Value;
                    dbi.db[i] = itemID.Attributes["db"].Value;
                    dbi.addr[i] = itemID.Attributes["addr"].Value;
                    dbi.name[i] = itemID.Attributes["name"].Value;
                    i++;
                }

            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
                throw ex;
            }
        }

        public Simatic(Snap7XModel x)
        {
            try
            {
                this.IP = x.Device.Ip;
                this.name = x.Device.deviceName;

                string type = x.Device.Type;
                if (type.ToUpper() == "S7-300")
                {
                    this.Slot = 2;
                }
                else if (type.ToUpper() == "S7-400")
                {
                    this.Slot = 3;
                }

                int num = x.PLC.Num;

                dbi.id = new string[num];
                dbi.db = new string[num];
                dbi.addr = new string[num];
                dbi.name = new string[num];

                int i = 0;
                foreach (XItemsID itemID in x.PLC.itemsID)
                {
                    if (i >= num)
                        break;
                    dbi.id[i] = itemID.id.ToString();
                    dbi.db[i] = itemID.db;
                    dbi.addr[i] = itemID.addr;
                    dbi.name[i] = itemID.name;
                    i++;
                }

            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
                throw ex;
            }
        }

        public void Connect()
        {
            base.Open();
        }

        public void DisConnect()
        {
            base.Close();
        }

        /// <summary>
        /// 泛型读
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="itemid"></param>
        /// <param name="result">读取结果（成功或失败）</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public T Read<T>(int itemid, out bool result)
        {
            result = false;
            T ret= default(T);

            string typeName = typeof(T).Name;
            switch(typeName)
            {
                case "Boolean":
                    bool mybool;
                    lock(this)
                    {
                        result = ReadPLC(itemid, out mybool);
                    }                   
                    ret = (T)(Object)mybool;
                    break;
                case "Byte":
                    byte mybyte;
                    lock (this)
                    {
                        result = ReadPLC(itemid, out mybyte);
                    }                        
                    ret = (T)(Object)mybyte;
                    break;
                case "Byte[]":
                    byte[] myarray;
                    lock (this)
                    {
                        result = ReadPLC(itemid, out myarray);
                    }                        
                    ret = (T)(Object)myarray;
                    break;
                case "Int16":
                case "Int32":
                    int myint;
                    lock (this)
                    {
                        result = ReadPLC(itemid, out myint);
                    }                       
                    ret = (T)(Object)myint;
                    break;
                case "String":
                    string mystr;
                    lock (this)
                    {
                        result = ReadPLC(itemid, out mystr);
                    }                        
                    ret = (T)(Object)mystr;
                    break;
                case "Single":
                    float myfloat;
                    lock (this)
                    {
                        result = ReadPLC(itemid, out myfloat);
                    }                       
                    ret = (T)(Object)myfloat;
                    break;
                default:
                    throw new Exception(String.Format("类型名:{0}不支持！", typeName));
            }
            return ret;          
        }

        /// <summary>
        /// 泛型写
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="itemid"></param>
        /// <param name="value"></param>
        /// <returns>写入结果（成功或失败）</returns>
        /// <exception cref="Exception"></exception>
        public bool Write<T>(int itemid, T value)
        {
            bool ret = false;
            string typeName = typeof(T).Name;
            switch (typeName)
            {
                case "Boolean":
                    lock (this)
                    {
                        ret = WritePLC(itemid, (bool)(Object)value);
                    }                        
                    break;
                case "Byte":
                    lock (this)
                    {
                        ret = WritePLC(itemid, (byte)(Object)value);
                    }                        
                    break;
                case "Byte[]":
                    lock (this)
                    {
                        ret = WritePLC(itemid, (byte[])(Object)value);
                    }                        
                    break;
                case "Int16":
                case "Int32":
                    lock (this)
                    {
                        ret = WritePLC(itemid, (int)(Object)value);
                    }                    
                    break;
                case "String":
                    lock (this)
                    {
                        ret = WritePLC(itemid, (string)(Object)value);
                    }                        
                    break;
                case "Single":
                    lock (this)
                    {
                        ret = WritePLC(itemid, (float)(Object)value);
                    }                       
                    break;
                default:
                    throw new Exception(String.Format("类型名:{0}不支持！", typeName));
            }
            return ret;
        }

        public string GetItemName(int itemid)
        {
            return dbi.name[itemid];
        }

        public string GetDeviceName()
        {
            return this.name;
        }

        public string GetIPAddr()
        {
            return this.IP;
        }

        /// <summary>
        /// 支持INT/BOOL/BYTE
        /// BOOL或BYTE按INT读会多读一个字节！
        /// 建议按实际类型读
        /// </summary>
        /// <param name="itemid"></param>
        /// <returns>成功或失败</returns>
        private bool ReadPLC(int itemid,out bool value)
        {
            value = false;
            if (!IsConnected) return false;
            if (dbi.id == null)
                throw new Exception("PLC XML配置未初始化！");
            int startAddr;
            int bit;
            string type = this.GetType(dbi.addr[itemid], out startAddr, out bit);
            int dbNum = this.GetDBNum(dbi.db[itemid]);
            if (type == "bool")
            {
                if (this.plc_read_db(dbNum, startAddr, bit, out value))
                    return true;
                else
                    return false;
            }
            else
            {
                throw new Exception("ReadPLCBool Type Error " + type + " itemid=" + itemid);
            }
        }
        private bool ReadPLC(int itemid,out byte value)
        {
            value = 0;
            if (!IsConnected) return false;
            if (dbi.id == null)
                throw new Exception("PLC XML配置未初始化！");
            int startAddr;
            int bit;
            string type = this.GetType(dbi.addr[itemid], out startAddr, out bit);
            int dbNum = this.GetDBNum(dbi.db[itemid]);
            if (type == "byte")
            {
                if (this.plc_read_db(dbNum, startAddr, out value))
                    return true;
                else
                    return false;
            }
            else
            {
                throw new Exception("ReadPLCByte Type Error " + type + " itemid=" + itemid);
            }
        }
        private bool ReadPLC(int itemid,out int value)
        {
            value = 0;
            if (!IsConnected) return false;
            if (dbi.id == null)
                throw new Exception("PLC XML配置未初始化！");

            int startAddr;
            int bit;
            string type = this.GetType(dbi.addr[itemid], out startAddr, out bit);
            int dbNum = this.GetDBNum(dbi.db[itemid]);
            if (type == "int")
            {
                if (this.plc_read_db(dbNum, startAddr, out value))
                    return true;
                else
                    return false;
            }
            else
            {
                throw new Exception("ReadPLCInt Type Error " + type + " itemid=" + itemid);
            }
        }
        private bool ReadPLC(int itemid,out float value)
        {
            value = 0.0f;
            if (!IsConnected) return false;
            if (dbi.id == null)
                throw new Exception("PLC XML配置未初始化！");
            int startAddr;
            int bit;
            string type = this.GetType(dbi.addr[itemid], out startAddr, out bit);
            int dbNum = this.GetDBNum(dbi.db[itemid]);
            if (type == "real")
            {               
                if (this.plc_read_db(dbNum, startAddr, out value))
                    return true;
                else
                    return false;
            }
            else
            {
                throw new Exception("ReadPLCFloat Type Error " + type + " itemid=" + itemid);
            }
        }
        private bool ReadPLC(int itemid,out String value)
        {
            value = "";
            if (!IsConnected) return false;
            if (dbi.id == null)
                throw new Exception("PLC XML配置未初始化！");
            int startAddr;
            int bit;
            string type = this.GetType(dbi.addr[itemid], out startAddr, out bit);
            int dbNum = this.GetDBNum(dbi.db[itemid]);
            if (type == "string")
            {                
                if (this.plc_read_db(dbNum, startAddr,bit, out value))
                    return true;
                else
                    return false;
            }
            else if(type == "wstring")
            {
                if (this.plc_read_db_ws(dbNum, startAddr, out value))
                    return true;
                else
                    return false;
            }
            else
            {
                throw new Exception("ReadPLCString Type Error " + type + " itemid=" + itemid);
            }
        }
        private bool ReadPLC(int itemid,out byte[] value)
        {
            value = null;
            if (!IsConnected) return false;
            if (dbi.id == null)
                throw new Exception("PLC XML配置未初始化！");

            int startAddr;
            int bit;
            string type = this.GetType(dbi.addr[itemid], out startAddr, out bit);
            int dbNum = this.GetDBNum(dbi.db[itemid]);
            if (type == "byte" || type == "byte[]")
            {
                if (bit > 0)
                {
                    
                    if (this.plc_read_db(dbNum, startAddr, bit, out value))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    throw new Exception(" ReadPLCByteArray bit Error " + type + " itemid=" + itemid);
                }
            }
            else
            {
                throw new Exception(" ReadPLCByteArray Type Error " + type + " itemid=" + itemid);
            }
        }

        private bool WritePLC(int itemid, int value)
        {
            if ( !IsConnected) return false;
            if (dbi.id == null)
                throw new Exception("PLC XML配置未初始化！");
            int startAddr;
            int bit;
            string type = this.GetType(dbi.addr[itemid], out startAddr, out bit);
            int dbNum = this.GetDBNum(dbi.db[itemid]);
            if (type == "int")
            {
                if (this.plc_write_db(dbNum, startAddr, value))
                    return true;
                else
                    return false;
            }
            else
            {
                throw new Exception("WritePLC Type Error " + type + " itemid=" + itemid);
            }
        }
        private bool WritePLC(int itemid, bool value)
        {
            if (!IsConnected) return false;
            if (dbi.id == null)
                throw new Exception("PLC XML配置未初始化！");
            int startAddr;
            int bit;
            string type = this.GetType(dbi.addr[itemid], out startAddr, out bit);
            int dbNum = this.GetDBNum(dbi.db[itemid]);
            if (type == "bool")
            {
                if (this.plc_write_db(dbNum, startAddr, bit, value))
                    return true;
                else
                    return false;
            }
            else
            {
                throw new Exception("WritePLC Type Error " + type + " itemid=" + itemid);
            }
        }
        private bool WritePLC(int itemid, byte value)
        {
            if (!IsConnected) return false;
            if (dbi.id == null)
                throw new Exception("PLC XML配置未初始化！");
            int startAddr;
            int bit;
            string type = this.GetType(dbi.addr[itemid], out startAddr, out bit);
            int dbNum = this.GetDBNum(dbi.db[itemid]);
            if (type == "byte")
            {
                if (this.plc_write_db(dbNum, startAddr, value))
                    return true;
                else
                    return false;
            }
            else
            {
                throw new Exception("WritePLC Type Error " + type + " itemid=" + itemid);
            }
        }
        private bool WritePLC(int itemid, string value)
        {
            if (!IsConnected) return false;
            if (dbi.id == null)
                throw new Exception("PLC XML配置未初始化！");
            int startAddr;
            int bit;
            string type = this.GetType(dbi.addr[itemid], out startAddr, out bit);
            int dbNum = this.GetDBNum(dbi.db[itemid]);
            if (type == "string")
            {
                if (this.plc_write_db(dbNum, startAddr, value))
                    return true;
                else
                    return false;
            }
            else if (type == "wstring")
            {
                if (this.plc_write_db_ws(dbNum, startAddr, value))
                    return true;
                else
                    return false;
            }
            else
            {
                throw new Exception("WritePLC Type Error " + type + " itemid=" + itemid);
            }
        }
        private bool WritePLC(int itemid, float value)
        {
            if (!IsConnected) return false;
            if (dbi.id == null)
                throw new Exception("PLC XML配置未初始化！");
            int startAddr;
            int bit;
            string type = this.GetType(dbi.addr[itemid], out startAddr, out bit);
            int dbNum = this.GetDBNum(dbi.db[itemid]);
            if (type == "float")
            {
                if (this.plc_write_db(dbNum, startAddr, value))
                    return true;
                else
                    return false;
            }
            else
            {
                throw new Exception("WritePLC Type Error " + type + " itemid=" + itemid);
            }
        }
        private bool WritePLC(int itemid, byte[] value)
        {
            if (!IsConnected) return false;
            if (dbi.id == null)
                throw new Exception("PLC XML配置未初始化！");
            int startAddr;
            int bit;
            string type = this.GetType(dbi.addr[itemid], out startAddr, out bit);
            int dbNum = this.GetDBNum(dbi.db[itemid]);
            if (type == "byte")
            {
                if (this.plc_write_db(dbNum, startAddr, value))
                    return true;
                else
                    return false;
            }
            else
            {
                throw new Exception("WritePLC Type Error " + type + " itemid=" + itemid);
            }
        }
    }

    #region Other Class
    public struct DBInfo
    {
        public string[] id;
        public string[] db;
        public string[] addr;
        public string[] name;
    }
    /// <summary>
    /// 西门子S7系列以太网通讯类
    /// </summary>
    public class Equip : BaseEquip //Equip
    {
        #region 字段定义

        private string _ip = "10.10.10.10";         //PLC IP Address
        private int _rack = 0;                      //机架号
        private int _slot = 0;                      //槽号,1200~1500设置为0,300系列设置为2

        private bool _isOpen = false;   //是否打开连接
        private int _errorCount = 0;    //记录读取失败次数
        private int _openErrorCount = 0;    //打开PLC失败的次数
        private S7Client _client = new S7Client();

        private bool State;

        private int[] _area = 
            { 
                 S7Consts.S7AreaPE,
                 S7Consts.S7AreaPA,
                 S7Consts.S7AreaMK,
                 S7Consts.S7AreaDB,
                 S7Consts.S7AreaCT,
                 S7Consts.S7AreaTM
            };
        private int[] _wordLen =
            {
                 S7Consts.S7WLBit,
                 S7Consts.S7WLByte,
                 S7Consts.S7WLChar,
                 S7Consts.S7WLWord,
                 S7Consts.S7WLInt,
                 S7Consts.S7WLDWord,
                 S7Consts.S7WLDInt,
                 S7Consts.S7WLReal,
                 S7Consts.S7WLCounter,
                 S7Consts.S7WLTimer
            };

        #endregion

        #region 属性定义

        /// <summary>
        /// IP
        /// </summary>
        public string IP
        {
            get
            {
                
                return this._ip;
            }
            set
            {
                this._ip = value;
            }
        }

        /// <summary>
        /// 机架号
        /// </summary>
        private int Rack
        {
            get
            {
                return this._rack;
            }
        }

        /// <summary>
        /// 插槽号
        /// </summary>
        public int Slot
        {
            get
            {
                return this._slot;
            }
            set
            {
                this._slot = value;
            }
        }

        public bool isOnline
        {
            get
            {
                return this.State;
            }
        }

        #endregion

        public int Swap(int a)
        {
            return (a >> 8 & 0xFF) + (a << 8 & 0xFF00);
        }
        public void Swap(byte[] b)
        {
            byte a;
            for(int i=0;i<b.Length;i+=2)
            {
                a=b[i+1];
                b[i + 1] = b[i];
                b[i] = a;
            }
        }

        #region 实现BaseEquip成员
                /// <summary>
        /// 建立与PLC的连接
        /// </summary>
        /// <returns></returns>
        public override bool Open()
        {
            lock (this)
            {
                if (this._isOpen == true)
                {
                    return true;
                }
                this.State = false;
                this._client.SetConnectionType(S7Client.CONNTYPE_BASIC);
                /////////////////////////////////////////////////////////////////////////////////
                int result = this._client.ConnectTo(this.IP, this.Rack, this.Slot);
                ///////////////////////////////////////////////////////////////////
                if (result != 0)
                {
                    Console.WriteLine(DateTime.Now.ToString()+" PLC连接失败：" + this.GetErrInfo(result));
                    this.State = false;
                    if (this._openErrorCount > 1)
                    {
                        System.Threading.Thread.Sleep(500);   //2次连接不上暂停10秒->1s
                        this._openErrorCount = 0;
                    }
                    this._openErrorCount++;
                    return this.State;
                }
                else
                {
                    Console.WriteLine(DateTime.Now.ToString() + " PLC连接成功!");
                    this.State = true;
                    this._isOpen = true;
                }
                return this.State;
            }
        }
        /// <summary>
        /// PLC数据读取方法
        /// </summary>
        /// <param name="block">要读取的块号</param>
        /// <param name="start">要读取的起始字</param>
        /// <param name="len">要读取的长度</param>
        /// <param name="buff"></param>
        /// <returns></returns>
        public override bool Read(string block, int start, int len, out object[] buff)
        {
            lock (this)
            {
                int sizeRead = 0;
                int iblock = Convert.ToInt32(block);
                buff = new object[len];
                for (int i = 0; i < len; i++)
                {
                    buff[i] = 0;
                }
                if (!this.Open())
                {
                    return false;
                }
                byte[] _buff = new byte[len * 2];
                int result = this._client.ReadArea(S7Consts.S7AreaDB, iblock, start * 2, len, S7Consts.S7WLInt, _buff, ref sizeRead);
                if (result != 0)
                {
                    Console.WriteLine("Read "+this.GetErrInfo(result) + "\t" + String.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now));
                    if (this._errorCount > 5)
                    {
                        this.Close();       //5次读取失败，关闭PLC，再次读取自动连接
                    }
                    this._errorCount++;
                }
                if (sizeRead != len * 2)
                {
                    Console.WriteLine(String.Format("block={0}, start={1}, len={2} 读取的字节数与设置的字数不成2倍关系!", block, start, len));
                }
                else
                {
                    for (int i = 0; i < len; i++)
                    {
                        byte[] curr = new byte[2];
                        //高低位对调
                        //curr[0] = _buff[i * 2];
                        //curr[1] = _buff[i * 2 + 1];
                        //buff[i] = this.Swap(BitConverter.ToInt16(curr, 0));
                        curr[1] = _buff[i * 2];
                        curr[0] = _buff[i * 2 + 1];
                        buff[i] = BitConverter.ToInt16(curr, 0);
                    }
                    //ICSharpCode.Core.LoggingService<Equip>.Warn("内部处理完毕" + "\t" + String.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now));
                }
                if (result != 0)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
        }
        /// <summary>
        /// 读IB输入寄存器
        /// </summary>
        /// <param name="Start"></param>
        /// <param name="Size"></param>
        /// <param name="Buffer"></param>
        /// <returns></returns>
        public bool ReadInput(int Start, int Size, byte[] Buffer)
        {
            lock (this)
            {
                int result = this._client.EBRead(Start, Size, Buffer);
                if (result != 0)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
        }
        public bool ReadBlock(string block, int start, int len, out byte[] buff)
        {
            lock (this)
            {
                int sizeRead = 0;
                int iblock = Convert.ToInt32(block);
                buff = new byte[len];
                //if (!this.Open())
                //{
                //    return false;
                //}
                if (!this.isOnline)
                {
                    return false;
                }
                //byte[] _buff = new byte[len * 2];
                int result = this._client.ReadArea(S7Consts.S7AreaDB, iblock, start, len, S7Consts.S7WLByte, buff, ref sizeRead);
                if (result != 0)
                {
                    Console.WriteLine("ReadBlock " + this.GetErrInfo(result) + "\t" + String.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now));
                    if (this._errorCount > 5)
                    {
                        this.Close();       //5次读取失败，关闭PLC，再次读取自动连接
                    }
                    this._errorCount++;
                }
                //if (sizeRead != len * 2)
                //{
                //    Console.WriteLine(String.Format("block={0}, start={1}, len={2} 读取的字节数与设置的字数不成2倍关系!", block, start, len));
                //}
                //else
                //{
                    //for (int i = 0; i < len; i++)
                    //{
                    //    byte[] curr = new byte[2];
                        //高低位对调
                        //curr[0] = _buff[i * 2];
                        //curr[1] = _buff[i * 2 + 1];
                        //buff[i] = this.Swap(BitConverter.ToInt16(curr, 0));
                        //curr[1] = _buff[i * 2];
                        //curr[0] = _buff[i * 2 + 1];
                        //buff[i] = BitConverter.ToInt16(curr, 0);
                    //}
                    //ICSharpCode.Core.LoggingService<Equip>.Warn("内部处理完毕" + "\t" + String.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now));
               // }
                if (result != 0)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
        }
        /// <summary>
        /// PLC数据写入方法
        /// </summary>
        /// <param name="block">要写入的块号</param>
        /// <param name="start">起始字</param>
        /// <param name="buff">要写入PLC的数据</param>
        /// <returns>写入成功返回true，失败返回false</returns>
        public override bool Write(int block, int start, object[] buff)
        {
            lock (this)
            {
                if (!this.Open())
                {
                    return false;
                }
                int iblock = Convert.ToInt32(block);
                int sizeWrite = 0;
                byte[] _buff = new byte[buff.Length * 2];
                for (int i = 0; i < buff.Length; i++)
                {
                    System.Int16 value = 0;
                    System.Int16.TryParse(buff[i].ToString(), out value);
                    byte[] curr = BitConverter.GetBytes(value);
                    //高低位对调
                    _buff[i * 2] = curr[1];
                    _buff[i * 2 + 1] = curr[0];
                }
                int result = this._client.WriteArea(S7Consts.S7AreaDB, iblock, start * 2, buff.Length, S7Consts.S7WLInt, _buff, ref sizeWrite);
                if (result != 0)
                {
                    Console.WriteLine("Write "+this.GetErrInfo(result) + "\t" + String.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now));
                }

                if (sizeWrite != buff.Length * 2)
                {
                    Console.WriteLine(String.Format("block={0}, start={1}, len={2} 写入的字节数与设置的字数不成2倍关系!", block, start, buff.Length));
                }
                if (result != 0)
                {
                    return false;
                }
                return true;
            }
        }
        /// <summary>
        /// 写QB输出寄存器
        /// </summary>
        /// <param name="Start"></param>
        /// <param name="Size"></param>
        /// <param name="Buffer"></param>
        /// <returns></returns>
        public bool WriteOutput(int Start, int Size, byte[] Buffer)
        {
            lock (this)
            {
                int result = this._client.ABWrite(Start, Size, Buffer);
                if (result != 0)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
        }
        public bool WriteBlock(int block, int start, byte[] buff)
        {
            lock (this)
            {
                if (!this.Open())
                {
                    return false;
                }
                int iblock = Convert.ToInt32(block);
                int sizeWrite = 0;
                //byte[] _buff = new byte[buff.Length * 2];
                //for (int i = 0; i < buff.Length; i++)
                //{
                //    System.Int16 value = 0;
                //    System.Int16.TryParse(buff[i].ToString(), out value);
                //    byte[] curr = BitConverter.GetBytes(value);
                //    //高低位对调
                //    _buff[i * 2] = curr[1];
                //    _buff[i * 2 + 1] = curr[0];
                //}
                int result = this._client.WriteArea(S7Consts.S7AreaDB, iblock, start, buff.Length, S7Consts.S7WLByte, buff, ref sizeWrite);
                if (result != 0)
                {
                    Console.WriteLine("WriteBlock " + this.GetErrInfo(result) + "\t" + String.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now));
                }

                //if (sizeWrite != buff.Length * 2)
                //{
                //    Console.WriteLine(String.Format("block={0}, start={1}, len={2} 写入的字节数与设置的字数不成2倍关系!", block, start, buff.Length));
                //}
                if (result != 0)
                {
                    return false;
                }
                return true;
            }
        }
        /// <summary>
        /// 断开与PLC的连接
        /// </summary>
        public override void Close()
        {
            lock (this)
            {
                try
                {
                    int result = this._client.Disconnect();
                    if (result != 0)
                    {
                        Console.WriteLine("PLC关闭失败：" + this.GetErrInfo(result));
                    }
                    else
                    {
                        Console.WriteLine("PLC已断开连接!");
                        this._errorCount = 0;
                        this.State = false;
                        this._isOpen = false;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("PLC关闭失败：" + ex.Message);
                }
            }
        }

        #endregion

        #region 辅助方法

        /// <summary>根据错误代码返回错误信息
        /// 例如int errCode=ActiveConn(1);  sring errInfo = GetErrInfo(err);
        /// </summary>
        /// <param name="errCode">错误码</param>
        /// <returns>错误信息</returns>
        public string GetErrInfo(int errCode)
        {
            if (errCode == 0)
                return " Success(" + this._client.ExecutionTime.ToString() + " ms)";
            else
                return this._client.ErrorText(errCode);
        }

        #endregion
    }

    public abstract class BaseEquip
{
	/// <summary>
    /// 打开设备
    /// </summary>
    /// <returns>成功返回true，失败返回false</returns>
	public abstract bool Open();
	/// <summary>
    /// 读取信息
    /// </summary>
    /// <param name="block">数据块</param>
    /// <param name="start">起始地址</param>
    /// <param name="len">长度</param>
    /// <param name="buff">读取返回信息</param>
    /// <returns>成功返回true，失败返回false</returns>
    public abstract bool Read(string block, int start, int len, out object[] buff);
    /// <summary>
    /// 写入信息
    /// </summary>
    /// <param name="block">数据块</param>
    /// <param name="start">起始地址</param>
    /// <param name="buff">要写入的数据</param>
    /// <returns>成功返回true，失败返回false</returns>
    public abstract bool Write(int block, int start, object[] buff);
    /// <summary>
    /// 关闭设备
    /// </summary>
    public abstract void Close();
}
    #endregion
}
